package com.devmindset.legoframework.client.screen;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.allen_sauer.gwt.log.client.Log;
import com.devmindset.legoframework.client.mvc.LegoComponent;
import com.devmindset.legoframework.client.mvc.EventMonitorManager;
import com.devmindset.legoframework.client.mvc.LegoEvent;
import com.devmindset.legoframework.client.mvc.metadata.ComponentStory;
import com.devmindset.legoframework.client.mvc.metadata.HasStory;
import com.devmindset.legoframework.client.mvc.metadata.ScreenStory;
import com.google.gwt.user.client.ui.RootPanel;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.widgets.layout.Layout;

/**
 * Represents a screen or view in the application.<br/><br/>
 * 
 * Screen specify themselves in two ways:<br/><br/>
 * 
 * - Knows to draw their layout by means of {@link #drawLayout()} method
 * - Knows where to place the declared components by means of {@link #drawLayout()} method<br/><br/>
 * 
 * The supported components are declared via the {@link ScreenStory#consistsOf(LegoComponent...)} method.<br/><br/>
 * 
 * @author <a href="mailto:ice.penov@devmindset.com">Ice Penov</a>
 */
@SuppressWarnings("unchecked")
public abstract class ApplicationScreen implements HasStory<ScreenStory> {
	
	private Map<Class, Boolean> activationMap;
	private List<LegoComponent> components = new ArrayList<LegoComponent>();
	private Map<LegoPlaceholder, Class> placeholderMap;
  
  protected void initialize() {
	  activationMap = new HashMap<Class, Boolean>();	  
	  LegoComponent[] initialComponents = this.myStory().getInitialState();
	  for (LegoComponent component : initialComponents) {
	    activationMap.put(component.getClass(), false);	      
	  }
	  placeholderMap = new HashMap<LegoPlaceholder, Class>();
//	  PlaceholderMapping[] placeholders = this.myStory().getPlaceholderMappings();
//	  for (PlaceholderMapping mapping : placeholders) {
//	  	if (mapping.getPlaceholder() != null) {
//		  	placeholderMap.put(mapping.getPlaceholder(), null);	  		
//	  	}
//	  }
  }
  
  /**
   * Adds a component to this screen.
   * <br/><br/>
   * 
   * @param component the component to be added.
   */
  protected void addComponent(LegoComponent component) {
	  if (!components.contains(component)) {
		  components.add(component);
	  }
  }
  
  protected List<LegoComponent> getComponents() {
	  return components;
  }
  
  /**
   * Draws the {@link Layout} for this {@link ApplicationScreen}.
   * 
   * @return the layout
   */
  protected abstract Layout drawLayout();
  
  /**
   * Place for the screen to state the message to be displayed while loading
   * the contents of the screen.
   * 
   * @return the loading message
   */
  protected abstract String loadingMessage();
    
  /**
   * Determines whether the passed component is an initial component
   * for this screen.
   */
  private boolean isInitialComponent(LegoComponent component) {
	  boolean flag = false;
	  LegoComponent[] initialComponents = this.myStory().getInitialState();
	  for (LegoComponent initialComponent : initialComponents) {
		  if (component.getClass().equals(initialComponent.getClass())) {
			  flag = true;
		  }
	  }
	  return flag;
  }
  
  /**
   * Determines whether the components for this screen
   * are ready to be displayed.
   */
  private boolean isScreenReady() {
	  boolean flag = true;
	  for (Class aClass : activationMap.keySet()) {
		  if (!activationMap.get(aClass)) {
			  flag = false;
			  break;
		  }
	  }
	  return flag;
  }
  
  /**
   * Internal method by which the component signals the completion
   * of their internal rendering and service async calls.
   * 
   * Considers only components required for one screen to present
   * itself in its initial state.
   */
  protected void signalCompletion(LegoComponent component) {
	  if (isInitialComponent(component) && !isScreenReady()) {
		  activationMap.put(component.getClass(), true);
		  //check if the screen is ready
		  if (isScreenReady()) {
		    RootPanel.get("loadingWrapper").setVisible(false);		    
			  ApplicationScreenManager.placePreparedLayout();
			  afterScreenReady();
		  }
	  }
  }
  
  protected void afterScreenReady() {
    ;//does nothing; provides a hook to extending classes.
  }
  
  /**
   * Fires the construction events for this screen.
   */
  protected void fireConstructionEvents() {
    ScreenStory story = this.myStory();
    if (story != null) {
      LegoComponent[] components = story.getInitialState();    
      if (components != null) {
        for (int i = 0; i < components.length; i++) {
          LegoComponent componentInstance = components[i];
          ComponentStory componentStory = componentInstance.myStory();
          if (componentStory != null) {
            Enum<?> constructionEvent = componentStory.getConstructedBy();
            if (constructionEvent != null) {
              EventMonitorManager.dispathEvent(new LegoEvent(constructionEvent));
              Log.debug("FIRED EVENT: [ " + constructionEvent + " ] as part of activation process of screen: [ " 
                  + this.myStory() + " ]");            
            } else {
            	String msg = "Needed constructedBy clause missing in the ComponentStory for component [ " 
                + componentInstance.myStory() + " ]"; 
              Log.error(msg);
              SC.warn(msg);
            }
          } else {
          	String msg = "Missing needed ComponentStory for component [ " + componentInstance.myStory() + " ]";
            Log.error(msg);
            SC.warn(msg);
          }
        }
      } else {
      	String msg = "Missing needed consistsOf clause in the ScreenStory for screen [ " + this.myStory() + " ]";
        Log.error(msg);
        SC.warn(msg);
      }
    } else {
    	String msg = "Missing needed ScreenStory for screen [ " + this.myStory() + " ]";
      Log.error(msg);
      SC.warn(msg);
    }
  }

	public Map<LegoPlaceholder, Class> getPlaceholderMap() {
		return placeholderMap;
	}  
        
}